package com.allen.study.test_common.interceptor;

import com.allen.study.test_common.annotation.RepeatSubmit;
import com.allen.study.test_common.exception.CustomRuntimeException;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 防止重复提交拦截器
 *
 * @author ruoyi
 */
//3种拦截器，都是http拦截器，在处理业务逻辑之前对http请求信息进行处理，比如获取请求头，请求参数，设置请求头，请求参数等等
//（1）HandlerInterceptor是最常规的，其拦截的http请求是来自于客户端浏览器之类的，是最常见的http请求拦截器；
//（2）ClientHttpRequestInterceptor是对RestTemplate的请求进行拦截的，在项目中直接使用restTemplate.getForObject的时候，会对这种请求进行拦截，经常被称为：RestTempalte拦截器或者Ribbon拦截器；
//（3）RequestInterceptor常被称为是Feign拦截器，由于Feign调用底层实际上还是http调用，因此也是一个http拦截器，在项目中使用Feign调用的时候，可以使用此拦截器；

// 这里用到了模板方法，因为除了判断重复提交，可能还有其他各种拦截判断
@Component//也是这个注解
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
{
    /**
     * @MethodName: preHandle
     * @Author: AllenSun
     * @Date: 2023/4/5 上午12:27
     *
     * （1）preHandle前置处理：在业务处理器处理请求之前被调用
     * （2）postHandle中置处理：在业务处理器处理请求执行完成后，生成视图之前执行。
     * 后处理（调用了Service并返回ModelAndView，但未进行页面渲染），有机会修改ModelAndView ，现在这个很少使用了
     * （3）afterCompletion后置处理：在DispatcherServlet完全处理完请求后被调用，可用于清理资源等
     *
     * 三个参数
     * （1）HttpServletRequest
     * （2）HttpServletResponse
     * （3）handler：匹配对应的Controller处理器
     *
     * 返回值
     * （1）true：表示继续流程（如调用下一个拦截器或处理器）
     * （2）false：表示流程中断（如登录检查失败），不会继续调用其他的拦截器或处理器，此时我们需要通过response来产生响应；
     *
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        // instanceof：判断handler对象是否是特定类HandlerMethod的一个实例
        // HandlerMethod类用于封装控制器方法信息，包含类信息、方法Method对象、参数、注解等信息，具体的接口请求是可以根据封装的信息调用具体的方法来执行业务逻辑；
        if (handler instanceof HandlerMethod)
        {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取Controller处理器里的方法
            Method method = handlerMethod.getMethod();
            // 判断方法上有没有RepeatSubmit注解
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
            if (annotation != null)
            {
                // 如果有RepeatSubmit注解
                // 调用isRepeatSubmit判断有没有重复提交，这个方法交给子类去具体实现（模板方法）
                if (this.isRepeatSubmit(request, annotation))
                {
                    // 如果确实重复提交了，就返回封装error的AjaxResult结果
                    throw new CustomRuntimeException("请勿重复提交");
                    // AjaxResult ajaxResult = AjaxResult.error(annotation.message());
                    // ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
                    // 中断检查流程，不会调用其他拦截器
                    // return false;
                }
            }
            return true;
        }
        else
        {
            return true;
        }
    }

    /**
     * 验证是否重复提交由子类实现具体的防重复提交的规则
     *
     * @param request
     * @return
     * @throws Exception
     */
    public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
}
